package cn.ac.iie.kgmanager.service.impl;

import cn.ac.iie.kgmanager.dao.RelationDao;
import cn.ac.iie.kgmanager.domain.Relation;
import cn.ac.iie.kgmanager.service.RelationService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.neo4j.driver.v1.Value;
import org.neo4j.driver.v1.types.Node;
import org.neo4j.driver.v1.types.Path;
import org.neo4j.driver.v1.types.Relationship;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
@AllArgsConstructor
@Slf4j
public class RelationServiceImpl implements RelationService {

    private final RelationDao relationDao;

    /**
     * 保存关系
     *
     * @param relation 关系
     * @return 关系id
     */
    @Override
    public String saveRelation(Relation relation) {
        //查询关系是否存在如果存在则返回id，否则插入再返回
        List<Relation> relations = getRelationByCondition(relation, 10, 1, 1);
        if (relations.size() > 0) {
            return relations.get(0).getId();
        } else {
            try {
                boolean flag = relationDao.insertRelation(relation.getStartId(), relation.getEndId(), relation.getName(),
                        relation.getProperties());
                return relation.getId();
            } catch (Exception e) {
                throw new RuntimeException("新增关系错误");
            }
        }
    }

    /**
     * 批量插入关系
     *
     * @param relationList 关系列表
     * @return 关系的
     */
    @Override
    public Set<String> batchSaveRelations(List<Relation> relationList) {
        Set<String> ids = new HashSet<>();

        for (Relation relation : relationList) {
            String id = saveRelation(relation);
            if (StringUtils.isNotBlank(id)) {
                ids.add(id);
            }
        }
        return ids;
    }


    /**
     * 根据生成的关系id删除关系
     *
     * @param id 关系id
     * @return 删除成功的关系id
     */
    @Override
    public String deleteRelationById(String id) {
        boolean deleteFlag = relationDao.deleteById(id);
        if (deleteFlag) {
            return id;
        } else {
            return null;
        }
    }

    @Override
    public List<Relation> getRelationByNodes(String[] ids) {
        List<Value> relation = relationDao.findAllRelationByNodes(ids);
        return valuesTransToEntity(relation);
    }

    /**
     * 批量删除关系
     *
     * @param ids 关系id列表
     * @return 删除成功的关系id列表
     */
    @Override
    public int batchDeleteRelations(String[] ids) {
        boolean deleteFlag = relationDao.batchDeleteByIds(ids);
        if (deleteFlag) {
            return ids.length;
        } else {
            return 0;
        }
    }


    @Override
    public List<Relation> getRelationByCondition(Relation relation, Integer pageSize, Integer pageNum, Integer depth) {
        List<Value> valueList = relationDao.findRelationByCondition(relation.getEndId(), relation.getStartId(), relation.getName(),
                relation.getProperties(), pageNum, pageSize, depth);
        return valuesTransToEntity(valueList);
    }

    @Override
    public int countRelationByCondition(Relation relation, Integer depth) {
        return relationDao.countByCondition(relation.getEndId(), relation.getStartId(), relation.getName(),
                relation.getProperties(), depth);
    }

    @Override
    public Relation getRelationById(String relationId) {
        Value value = relationDao.findRelationById(relationId);
        List<Value> list = new LinkedList<>();
        list.add(value);
        List<Relation> relations = valuesTransToEntity(list);
        return relations.get(0);
    }

    @Override
    public boolean updateRelation(Relation relation) {
        return relationDao.updateRelation(relation);
    }

    @Override
    public int batchUpdateRelations(List<Relation> relationList) {
        boolean updateFlag = relationDao.batchUpdateRelation(relationList);
        if (updateFlag) {
            return relationList.size();
        } else {
            return 0;
        }
    }


    /**
     * 把values转换成实体
     *
     * @param valueList neo4j返回list
     * @return 关系实体列表
     */
    private List<Relation> valuesTransToEntity(List<Value> valueList) {

        Map<Long, String> mapping = new HashMap<>();
        List<Relation> relations = new LinkedList<>();
        if (valueList != null && !valueList.isEmpty()) {
            for (Value value : valueList) {
                Path path = value.asPath();
                Iterable<Node> nodes = path.nodes();
                Iterable<Relationship> relationships = path.relationships();

                log.info("path.length:{}", path.length());

                for (Node node : nodes) {
                    log.info("node:{}", node);
                    String entityId = node.get("id").asString();
                    long id = node.id();
                    log.info("node.id:{}", entityId);
                    mapping.put(id, entityId);
                }

                for (Relationship relationship : relationships) {
                    Relation relation = new Relation();

                    log.info("relation:{}", relationship);
                    Iterable<String> keys = relationship.keys();
                    long startNodeId = relationship.startNodeId();
                    long endNodeId = relationship.endNodeId();

                    if (mapping.containsKey(startNodeId) && mapping.containsKey(endNodeId)) {
                        String startIdStr = mapping.get(startNodeId);
                        String endIdStr = mapping.get(endNodeId);
                        relation.setStartId(startIdStr);
                        relation.setEndId(endIdStr);
                        relation.setName(relationship.type());


                        Map<String, Object> newPropertiesMap = new HashMap<>(relationship.asMap());

                        if (newPropertiesMap.containsKey("id")) {

                            relation.setId(newPropertiesMap.get("id").toString());
                        }

                        newPropertiesMap.remove("id");
                        newPropertiesMap.remove("startId");
                        newPropertiesMap.remove("endId");

                        relation.addProperties(newPropertiesMap);

                        relations.add(relation);
                    }

                }
            }
        }

        return relations;

    }


}
